package com.peng.jackson;

import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.peng.utils.JacksonUtil;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * JsonDeserializeSample
 *
 * @author lupeng
 * Created on 2022-04-10
 */
public class JsonDeserializeSample {

    private static final Logger logger = LoggerFactory.getLogger(JsonDeserializeSample.class);

    private static ObjectMapper objectMapper = new ObjectMapper();

    @Data
    public static class Bean1 {
        public int id;
        public String name;

        @JsonCreator
        public Bean1(
                @JsonProperty("id") int id,
                // 这个类中没有这个属性
                @JsonProperty("theName") String name) {
            this.id = id;
            this.name = name;
        }
    }


    public enum Bean11 {
        day, global;

        @JsonCreator
        public Bean11 c(String name) {
            for (Bean11 value : Bean11.values()) {
                if (StringUtils.equals(value.name(), name)) {
                    return value;
                }
            }
            return null;
        }
    }

    public static class Bean22 {
        public int id;
        public String name;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        @JsonIgnore
        public String getName() {
            return name;
        }

        @JsonProperty
        public void setName(String name) {
            this.name = name;
        }

        public Bean22() {
        }
    }


    @Test
    public void s111() throws JsonProcessingException {
        Bean22 bean2 = new Bean22();
        bean2.setId(11);
        bean2.setName("sddfasdfsa");
        System.out.println(JacksonUtil.toJson(bean2));

        String json = """
                {"id":11,"name":"sdfsdfasdf"}
                """.strip();
        Bean22 bean22 = JacksonUtil.fromJson(json, Bean22.class);
        System.out.println(bean22);
    }

    @Data
    public static class Bean111 {
        private Bean11 type;
    }

    @Test
    public void s11() throws JsonProcessingException {
        String json = """
                {
                    "type": "global"
                }
                """.strip();
        Bean111 bean111 = objectMapper.readValue(json, Bean111.class);
        System.out.println(bean111);
    }


    @Test
    public void s1() throws JsonProcessingException {
        String json = """
                {
                    "id":1,
                    "theName":"My bean"
                }
                """.strip();
        Bean1 bean1 = objectMapper.readValue(json, Bean1.class);
        System.out.println(bean1);
    }

    @Data
    public static class Bean2 {
        public int id;
        public String theName;

        private Map<String, Object> properties = new HashMap<>();

        // 将没有匹配到的字段放到Map中
        @JsonAnySetter
        public void add(String k, Object v) {
            properties.put(k, v);
        }
    }

    @Test
    public void s2() throws JsonProcessingException {
        String json = """
                {
                    "id":1,
                    "theName":"My bean",
                    "attr1":"val1",
                    "attr2": {
                        "k1":"v1"
                    }
                }
                """.strip();
        Bean2 bean2 = objectMapper.readValue(json, Bean2.class);
        System.out.println(bean2);
    }

    @Data
    public static class Bean3 {
        public int id;
        @JsonDeserialize(using = CustomDeserialize.class)
        public String theName;
    }

    public static class CustomDeserialize extends StdDeserializer<String> {
        public CustomDeserialize() {
            this(null);
        }

        public CustomDeserialize(Class<String> t) {
            super(t);
        }

        @Override
        public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
            String text = p.getText();
            return "CustomDeserialize: " + text;
        }
    }

    @Test
    public void s3() throws JsonProcessingException {
        String json = """
                {
                    "id":1,
                    "theName":"My bean"
                }
                """.strip();
        Bean3 bean3 = objectMapper.readValue(json, Bean3.class);
        System.out.println(bean3);
    }

    public static class Bean4 {
        public int id;
        public String firstName;

        @JsonAlias(value = {"fName", "first_name"})
        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getFirstName() {
            return firstName;
        }
    }

    @Test
    public void s4() throws JsonProcessingException {
        String json = """
                {
                    "id":1,
                    "first_name":"My bean"
                }
                """.strip();
        Bean4 bean4 = objectMapper.readValue(json, Bean4.class);
        System.out.println(bean4);
    }


    @AllArgsConstructor
    @JsonInclude(JsonInclude.Include.NON_NULL)
    public class Bean5 {
        public int id;
        public String name;
    }

    @Test
    public void s5() throws JsonProcessingException {
        Bean5 bean5 = new Bean5(1, null);
        System.out.println(objectMapper.writeValueAsString(bean5));
        // {"id":1,"name":null} 不开启
        // {"id":1} 开启之后
    }

    @AllArgsConstructor
    @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
    //@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.DEFAULT)
    public class PrivateBean {
        private int id;
        private String name;
    }

    @Test
    public void s6() throws JsonProcessingException {
        PrivateBean bean = new PrivateBean(1, "aaa");
        System.out.println(objectMapper.writeValueAsString(bean));
        // JsonAutoDetect.Visibility.DEFAULT {}
        // JsonAutoDetect.Visibility.ANY {"id":1,"name":"aaa"}
    }

    @Data
    public static class Zoo {
        private Animal animal;
    }

    @Data
    @JsonTypeInfo(
            use = JsonTypeInfo.Id.NAME,
            property = "type"
    )
    @JsonSubTypes({
            @JsonSubTypes.Type(value = Dog.class, name = "dog"),
            @JsonSubTypes.Type(value = Cat.class, name = "cat")
    })
    public abstract static class Animal {
        public String type;

        public abstract void call();
    }

    @Data
    @JsonTypeName("cat")
    public static class Cat extends Animal {
        @Override
        public void call() {
            System.out.println(super.getType() + "喵喵喵");
        }
    }

    @Data
    @JsonTypeName("dog")
    public static class Dog extends Animal {
        @Override
        public void call() {
            System.out.println(super.getType() + "汪汪汪");
        }
    }

    @Test
    public void s7() throws JsonProcessingException {
        String json = """
                {
                    "animal": {
                        "type": "cat"
                    }
                }
                """.strip();
        Zoo zoo = objectMapper.readValue(json, Zoo.class);
        zoo.animal.call();
    }

    @Data
    @AllArgsConstructor
    public class EventWithFormat {
        public String name;

        @JsonFormat(
                shape = JsonFormat.Shape.STRING,
                pattern = "yyyy-MM-dd HH:mm:ss",
                timezone = "GMT+8")
        public Date eventDate;
    }

    @Test
    public void s8() throws JsonProcessingException {
        EventWithFormat event = new EventWithFormat("aa", new Date());
        System.out.println(objectMapper.writeValueAsString(event));
        // {"name":"aa","eventDate":"2022-04-10 12:34:26"} 时间和当前时间对不上
        // 添加 timezone = "GMT+8" 之后时间正确
    }

    @Data
    @AllArgsConstructor
    public class UnwrappedUser {
        public int id;

        @JsonUnwrapped
        public Name name;

        @Data
        @AllArgsConstructor
        public static class Name {
            public String firstName;
            public String lastName;
        }
    }

    @Test
    public void s9() throws JsonProcessingException {
        // serialize
        UnwrappedUser user = new UnwrappedUser(1, new UnwrappedUser.Name("zhang", "san"));
        System.out.println(objectMapper.writeValueAsString(user));
        //{"id":1,"firstName":"zhang","lastName":"san"}
    }


    public class Views {
        public static class Public {
        }

        public static class Internal extends Public {
        }
    }

    @Data
    @AllArgsConstructor
    public class Item {
        @JsonView(Views.Public.class)
        public int id;

        @JsonView(Views.Public.class)
        public String itemName;

        @JsonView(Views.Internal.class)
        public String ownerName;
    }

    @Test
    public void s10() throws JsonProcessingException {
        Item item = new Item(2, "book", "John");
        System.out.println(objectMapper.writerWithView(Views.Public.class)
                .writeValueAsString(item));
        // {"id":2,"itemName":"book"}
    }
}
